package com.letoken.platform.pub.util;

//import sun.misc.BASE64Decoder;
//import sun.misc.BASE64Encoder;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.UUID;

/**
 * AESUtil
 */
public class AESUtil {

    public static final String SECRET_KEY = "AESSecretkey";

    //自己定义的秘钥,128bit即16位的随机串。也支持192,25bit,长度越长安全性越高，对应的加解密时间越长
    private final static String KEY_STR = "letokenwallet";// 可以写进配置
    private final Key key;
    private static AESUtil instance = new AESUtil();
    private static final String ALGORITHM = "AES";
    private static final String RANDOM_ALGORITHM = "SHA1PRNG";

    /**
     * 密码盐
     *
     * @return
     */
    public static String salt() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 带盐加密
     *
     * @param secret
     * @param content
     * @param salt
     * @return
     * @throws Exception
     */
    public static String encrypt(String secret, String content, String salt) throws Exception {
        return AESEncode(secret, MD5Util.MD5(content) + salt);
    }

    /**
     * 加密
     *
     * @throws Exception
     */
    public static String AESEncode(String secret, String content) throws Exception {
        // 1.获取秘钥
        SecretKey key = getKey(secret);
        // 2.根据指定算法AES自成密码器
        Cipher cipher = Cipher.getInstance("AES");
        // 3.初始化密码器，第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作，第二个参数为使用的KEY
        cipher.init(Cipher.ENCRYPT_MODE, key);
        // 4.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
        byte[] byte_encode = content.getBytes("utf-8");
        // 5.根据密码器的初始化方式--加密：将数据加密
        byte[] byte_AES = cipher.doFinal(byte_encode);
        // 6.将加密后的数据转换为字符串
        String AES_encode = new String(Base64.getEncoder().encode(byte_AES));
        return AES_encode;
    }

    /**
     * 解密 解密过程： 1.同加密1-4步 2.将加密后的字符串反纺成byte[]数组 3.将加密内容解密
     *
     * @throws Exception
     */
    public static String AESDecode(String secret, String content) throws Exception {
        // 1.根据字节数组生成AES密钥
        SecretKey key = getKey(secret);
        // 2.根据指定算法AES自成密码器
        Cipher cipher = Cipher.getInstance("AES");
        // 3.初始化密码器，第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作，第二个参数为使用的KEY
        cipher.init(Cipher.DECRYPT_MODE, key);
        // 4.将加密并编码后的内容解码成字节数组
        byte[] byte_content = Base64.getDecoder().decode(content);
        byte[] byte_decode = cipher.doFinal(byte_content);
        String AES_decode = new String(byte_decode, "utf-8");
        return AES_decode;
    }

    /**
     * SecretKey 生成
     *
     * @param secret
     * @return
     * @throws Exception
     */
    private static SecretKeySpec getKey(String secret) throws Exception {
        // 初始化16byte，128位存储空间
        byte[] keyBytes = new byte[16];
        // 0值填充
        Arrays.fill(keyBytes, (byte) 0x0);
        // 校验长度，填充secret
        byte[] passwordBytes = secret.getBytes("UTF-8");
        int length = passwordBytes.length < keyBytes.length ? passwordBytes.length : keyBytes.length;
        System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
        SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
        return key;
    }


    /**
     * 私有构造函数，防止通过实例化使用
     */
    private AESUtil() {
        try {
            KeyGenerator generator = KeyGenerator.getInstance(ALGORITHM);//Java的秘钥生产器，使用的是AES
            SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);//随机数的算法，NativePRNG和SHA1PRNG
            random.setSeed(KEY_STR.getBytes());//用我们设定的秘钥串作为随机数的种子,因为种子是我们固定的，产生的随机数也是固定的
            generator.init(random);
            key = generator.generateKey();//生成的秘钥，我们在加密解密需要用到相同秘钥
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("AES构造函数异常");
        }
    }

    public static AESUtil getInstance() {
        return instance;
    }

    /**
     * 对byte[]参数进行加密
     *
     * @param bytes 要加密的参数
     * @return 加密后的参数
     */
    private byte[] getEncCode(byte[] bytes) {
        byte[] result = null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");//获取算法实例
            cipher.init(Cipher.ENCRYPT_MODE, key);//初始化，入参为加密模式和秘钥
            result = cipher.doFinal(bytes);//进行加密
        } catch (Exception e) {
            throw new RuntimeException("AES加密异常");
        } finally {
            cipher = null;
        }
        return result;
    }

    /**
     * 对byte[]参数进行解密
     *
     * @param bytes 要解密的参数
     * @return 解密后的参数
     */
    private byte[] getDesCode(byte[] bytes) {
        byte[] result = null;
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES");//获取算法实例
            cipher.init(Cipher.DECRYPT_MODE, key);//初始化，入参为解密模式和秘钥
            result = cipher.doFinal(bytes);//进行解密
        } catch (Exception e) {
            throw new RuntimeException("AES解密异常");
        } finally {
            cipher = null;
        }
        return result;
    }

    /**
     * 对string参数进行加密
     *
     * @param str 要加密的参数
     * @return 加密后的参数
     */
    public String getEncString(String str) {
        Encoder base64en = Base64.getEncoder();
        byte[] input = null; //明文
        byte[] output = null; //密文
        String result = null;
        try {
            input = str.getBytes();
            output = getEncCode(input);
            result = base64en.encodeToString(output);

        } catch (Exception e) {
            throw new RuntimeException("AES解密异常，参数：" + str);
        } finally {
            input = null;
            output = null;
        }
        return result;
    }

    /**
     * 对String参数进行解密
     *
     * @param str 要解密的参数
     * @return 解密后的参数
     */
    public String getDesString(String str) {
        Decoder base64De = Base64.getDecoder();
        byte[] input = null; //密文
        byte[] output = null; //明文
        String result = null;
        try {
            input = base64De.decode(str);
            output = getDesCode(input);
            result = new String(output);

        } catch (Exception e) {
            throw new RuntimeException("AES解密异常，参数：" + str);
        } finally {
            input = null;
            output = null;
        }
        return result;
    }

}
